#include <unistd.h>
#include <iostream>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <zemb/Tracer.h>
#include <zemb/StrUtil.h>
#include <zemb/ArgUtil.h>
#include <zemb/Thread.h>
#include <zemb/DateTime.h>

using namespace zemb;

/* 这是自己设计的一个测试内核调度实时性的工具,类似于cyclictest,但比cyclictest更简单,更容易理解. */
static int s_interval = 0;
static int s_cycle = 0;
static volatile int s_finished = 0;

class Cyclic:public Runnable{
public:
    Cyclic(int threadID)
    {
        m_totalCycle = 0;
        m_threadID = threadID;
    }
    void run(Thread& thread)
    {
        while(thread.isRunning() && (m_totalCycle<=s_cycle))
        {
            m_totalCycle++;
            m_currTime=Time::fromMono();/* 记录线程投入运行的时间 */
            if(m_totalCycle!=1)
            {
                /* 计算从上次休眠到现在的时间间隔,即为CPU重新调度到本线程的时间
                 * 我们通过观察这个值，与我们设定的期望值(休眠时间)比较,就可以知道内核调度器的实时性能.
                 */
                m_diffTime = m_currTime - m_lastTime;
                if(m_totalCycle==2)
                {
                    m_minTime = m_diffTime;
                    m_maxTime = m_diffTime;
                }
                else
                {
                    Time tmp = m_diffTime-m_minTime;
                    if (tmp.secPart()<0)
                    {
                        m_minTime = m_diffTime;
                    }
                    tmp = m_maxTime - m_diffTime;
                    if (tmp.secPart() < 0)
                    {
                        m_maxTime = m_diffTime;
                    }
                }
            }
            
            m_totalTime = m_totalTime + m_diffTime;
            TRACE_DBG("[Thread%4d:%-5d %lldus]",m_threadID,m_totalCycle,m_diffTime.toMicroSec());
            m_lastTime = Time::fromMono();/* 记录线程睡眠之前的时间 */
            Thread::usleep(s_interval);/* 睡眠一定的时间,线程期望在睡眠这个时间间隔后,调度器能重新把CPU分给自己 */
        }

        
        long long total = m_totalTime.toMicroSec();
        int avg = total / (m_totalCycle-1);
        TRACE_YELLOW("Thread[%4d] cycles:%5d, min:%6lldus, max:%6lldus, avg:%6dus",m_threadID,m_totalCycle-1,
                      m_minTime.toMicroSec(),m_maxTime.toMicroSec(),avg);
        s_finished++;
    }

private:
    Time m_currTime,m_lastTime,m_diffTime;
    Time m_totalTime,m_maxTime,m_minTime;
    int m_totalCycle;
    int m_threadID;
};

void help(char* exename)
{
    PRINT_YELLOW("%s [--help] [-d <debug_level>] [-n <threads>] [-i <interval_us>] [-c <cycles>] [-p <policy>]",exename);
    PRINT_YELLOW("policy:  0-SCHED_OTHER    1-SCHED_FIFO    2-SCHED_RR");
}

int main(int argc, char* argv[])
{
    std::string argValue;
    ArgOption  option;
    Thread scmServiceThread;
    Time startTime = Time::fromMono();
    Tracer::getInstance().setLevel(TRACE_LEVEL_INFO);
    Tracer::getInstance().addSink(std::make_shared<STDSink>()).start();
    
    if(!option.parseArgs(argc,argv) ||option.hasOption("h")>=0 || option.hasOption("help")>=0)	/* 解析参数 */
    {
        help(argv[0]);
        return RC_ERROR;
    }
    
    if (option.hasOption("d")>0)
    {
        int level = std::stoi(option.getValue("d")[0]);
        Tracer::getInstance().setLevel(level);
    }

    int threadNums;
    if (option.hasOption("n")>0)
    {
        threadNums = std::stoi(option.getValue("n")[0]);
        threadNums = MAX(1,threadNums);
    }
    else
    {
        help(argv[0]);
        return RC_ERROR;
    }

    if (option.hasOption("i")>0)
    {
        s_interval = std::stoi(option.getValue("i")[0]);
        s_interval = MAX(1,s_interval);
    }
    else
    {
        
        help(argv[0]);
        return RC_ERROR;
    }

    if (option.hasOption("c")>0)
    {
        s_cycle = std::stoi(option.getValue("c")[0]);
        s_cycle = MAX(1,s_cycle);
    }
    else
    {
        help(argv[0]);
        return RC_ERROR;
    }

    int policy=0;
    if (option.hasOption("p")>0)
    {
        policy = std::stoi(option.getValue("p")[0]);
        policy = CLIP(0,policy,2);
    }
    else
    {
        help(argv[0]);
        return RC_ERROR;
    }

    /* 初始化实时抢占 */
    std::vector<std::shared_ptr<PThread>> threadVect;
    std::vector<std::shared_ptr<Cyclic>> cyclicVect;	
    for (int i=0; i<threadNums; i++)
    {
        cyclicVect.push_back(std::make_shared<Cyclic>(i));
        switch(policy){
        case 1:
        case 2:
        {
            threadVect.push_back(std::make_shared<PThread>(policy,80));
            break;
        }
        default:
            threadVect.push_back(std::make_shared<PThread>());
            break;
        }
    }

    for (int i=0; i<threadNums; i++)
    {
        threadVect[i]->start(*cyclicVect[i]);
    }
    
    while(1)
    {
        Thread::msleep(1000);
        //TRACE_GREEN("%s is running  @ %s.",argv[0],CSTR(DateTime::getDateTime().toString()));
        if(s_finished==threadNums)
        {
            TRACE_GREEN("%s is finished @ %s.",argv[0],CSTR(DateTime::getDateTime().toString()));
            break;
        }
    }
    
    return 0;
}
